
import {
    MeshNormalMaterial, PlaneGeometry, Mesh, 
    Box3Helper,Box3,
     Vector3,Quaternion,
    MeshLambertMaterial, MeshPhongMaterial,  MeshBasicMaterial,
    MeshStandardMaterial, MeshPhysicalMaterial, MeshToonMaterial,ShaderMaterial,
    TextureLoader,
    RepeatWrapping,
    LineCurve3,
    LineDashedMaterial,
    QuadraticBezierCurve3,
    DoubleSide,
    EquirectangularReflectionMapping,
    SpotLight,HemisphereLight,
    DirectionalLight,
    Color,
    Group,
    Euler,
    Matrix4,
    LineCurve,
    OrthographicCamera,
    Scene,
    AmbientLight,
    Vector2
} from "three";
import {initScene} from "./initScene.js";
import {curvesToMeshGeometry, exportGLB, getArowAixs, instanceObject3DOnPoints} from "../utils/geometries.js";


import { CustomSinCurve, PolyLineCurve3, CircleCurve ,} from "../utils/curves.js";
import GUI from "three/examples/jsm/libs/lil-gui.module.min.js";
import { getPetalMat, petalStdDecorator } from "../utils/materials.js";
import { canvasToImage } from "../utils/image.js";


const  gui = new GUI() ;
const curveMeshFolder = gui.addFolder('花瓣');

const box3 = new Box3();
const box3Helper= new Box3Helper(box3,0xff8800);


const textureLoader = new TextureLoader() ;


const {cos,sin,PI,max} = Math;
const stage = initScene({
    cameraPosition: new Vector3(3, 4, 5),
});
const { scene, renderer } = stage;

const env = textureLoader.load('https://000231.s3.bitiful.net/env/00004-199736278.png');
env.mapping = EquirectangularReflectionMapping;
 scene.environment = env ;
 scene.background = env ;
const l1  = new SpotLight(0xffffff,1,30) ;
const l2 = new DirectionalLight(0xffffff, 1.8);
const axes = getArowAixs(5,1);

l1.position .set (10,10,10)
l2.position .set (10,10,10)
scene.add(axes,l1,l2 ) ;

const map = new TextureLoader().load('/noise_1.jpg');
map.wrapS = map.wrapT = RepeatWrapping;
const basicMat = new MeshBasicMaterial({
    map,
    side:DoubleSide
})


const dashMat = new LineDashedMaterial(
    {
        dashSize: .1,
        gapSize: .1,
        scale: 1,
        color: 0xff00ff

    });




const curveParams = {
    // mainCurve,
    // profileCurve,
    leafDepth:0.4,
    leafWidth: 0.4,

    leafMid: 0,// 足以证明这是一个曲线转网格
    leafLong: 1.5,

    segments1: 32,
    segments2: 32,
    closed: false,
    noiseScale: 70,
    noiseOffset: 0,
    bgColor: '#ffff00',
    color: '#ff00ff',
}


// 测试曲线转网格

const flowerUniform ={
    bgColor: { value: new Color(0.8, 0.8, .8) },
    noiseScale: { value: curveParams.noiseScale },
    noiseOffset: { value: curveParams.noiseOffset },//目测 0得时候最好

}
const beforeCompile = petalStdDecorator(flowerUniform);
const pbrFlowerMat = new MeshPhysicalMaterial({
    color: 0xffffff,
    roughness: 0.8,
    metalness: 0,
    // flatShading: true,
    side: DoubleSide,
    
});

pbrFlowerMat.onBeforeCompile = beforeCompile;

const  profileCurve = new QuadraticBezierCurve3(new Vector3(0,curveParams. leafWidth,0),new Vector3(-curveParams.leafDepth,0,0),new Vector3(0,-curveParams.leafWidth,0))
const  mainCurve = new QuadraticBezierCurve3(new Vector3(0,0,0),new Vector3(0,0,0),new Vector3(curveParams.leafLong,0,0))

// 外挂式曲线半径的实现
function getRadius (u) {
    return  Math.sin(u * Math.PI );
}
function getRadiusArray (u) {
    // @ts-ignore
    if(!this.cacheRadiusArray){
    // @ts-ignore
        this.cacheRadiusArray = [];
    }
    // @ts-ignore
    if( this.cacheRadiusArray.length >0){
    // @ts-ignore
        return this.cacheRadiusArray;
    }
    for (let index = 0; index <= u; index++) {
    // @ts-ignore
        this.cacheRadiusArray.push(this.getRadius(index / u  ));
    }
    // @ts-ignore
    return this.cacheRadiusArray;
}
    // @ts-ignore
mainCurve.getRadius = getRadius;
    // @ts-ignore
mainCurve.getRadiusArray = getRadiusArray;
let   curvesGeo = curvesToMeshGeometry(mainCurve,profileCurve,{segments1:32,segments2:32,closed1:false})

let lotusGroup = new Group()
const guiConfig = {
    axis: true,
    mat:'basic',
    minRadius: 0.1,
    maxRadius: 0.3,
    flowerFactor: 1,
    flowerColor: '#ffffff',
    layers: 4,
    dy: 0.01,
    dCount: 2,
    minPolar: 0.3,
    maxPolar: 0.9,
    maxCount: 12,
    baking:false,
    needShot : false,
    reset () {
    },
    shot(){
        guiConfig.needShot = true;
    },
    bake(){ guiConfig.baking = true},
    save(){
        exportGLB(lotusGroup,'lotus.glb')
    }
}

/**
 * 
 * @param {number} layers 
 * @param {number} minRadius 
 * @param {number} maxRadius 
 * @param {number} dy 每层高度差值
 * @param {number} dCount  花瓣数量差值 
 * @param {number} minPolar 花瓣最小仰角
 * @param {number} maxPolar 花瓣最大仰角
 * @param {number} maxCount 花瓣最大数量
 * @returns void

 */
function initFlower(layers = 3,minRadius = 0.01,maxRadius = 0.3,dy=0.5,dCount=3,minPolar =0,maxPolar=1.5,maxCount=12) {
    
    let phi=0,y=0,radius=0,count = 0,theta=0;
    const pos = new Vector3(),euler = new Euler(),quat = new Quaternion(),scale = new Vector3(1,1,1),v2=new Vector3();

    for (let tier = 0; tier <layers; tier++) {
      phi = minPolar +(maxPolar -minPolar) * tier/(layers -1);
      y = 0.5 + tier * dy;
      radius =   minRadius + (maxRadius - minRadius)/(tier+1);
      count = maxCount- tier*dCount;
      scale.set(1.5-y*.5,1,1-count *0.01)
      for (let i = 1; i <= count; i++) {
        let m = new Mesh( curvesGeo, pbrFlowerMat)
        theta =2* PI * i/count  ;
        pos.set(radius*cos(theta ),y,-radius*sin(theta));
        v2.set(pos.x, phi, pos.z)// 
        // 这里遇到四元数旋转的问题了，看来是没有搞清楚四元数旋转的特性，单点或者线是没问题，一个面的话，还差东西
        euler.set(0,theta,phi);
        m.position.copy(pos);
        m.rotation.copy(euler);
        m.scale.copy(scale);
        lotusGroup.add(m);
      }
    }
  }
const petals = new Mesh( curvesGeo, pbrFlowerMat);
  function initInstancedFlower(layers = 3,minRadius = 0.01,maxRadius = 0.3,dy=0.5,dCount=3,minPolar =0,maxPolar=1.5,maxCount=12) {
   const matrixArr = [],mat4 = new Matrix4(); 
    let phi=0,y=0,radius=0,count = 0,theta=0,meshIndex = 0;
    const pos = new Vector3(),euler = new Euler(),quat = new Quaternion(),scale = new Vector3(1,1,1),v2=new Vector3();

    for (let tier = 0; tier <layers; tier++) {
      phi = minPolar +(maxPolar -minPolar) * tier/(layers -1);
      y = 0.5 + tier * dy;
      radius =   minRadius + (maxRadius - minRadius)/(tier+1);
      count = maxCount- tier*dCount;
      scale.set(1.5-y*.5,1,max(0.001,1-count *0.01))
      for (let i = 1; i <= count; i++) {
        theta =2* PI * i/count  ;
        pos.set(radius*cos(theta ),y,-radius*sin(theta));
        v2.set(pos.x, phi, pos.z)// 
        // 这里遇到四元数旋转的问题了，看来是没有搞清楚四元数旋转的特性，单点或者线是没问题，一个面的话，还差东西
        // quat.setFromUnitVectors(orient,v2.normalize())
        euler.set(0,theta,phi);
        quat.setFromEuler(euler);
        mat4.compose(pos,quat,scale);
        matrixArr.push(mat4.clone() )
        meshIndex++;// 也只好手动计数了
      }
    }
    const  g  = instanceObject3DOnPoints(petals, matrixArr);
    return g
  }
  
//   initFlower(guiConfig.layers,guiConfig.minRadius,guiConfig.maxRadius,guiConfig.dy,guiConfig.dCount,guiConfig.minPolar,guiConfig.maxPolar,guiConfig.maxCount)
let lotusInstanceGroup = initInstancedFlower(guiConfig.layers,guiConfig.minRadius,guiConfig.maxRadius,guiConfig.dy,guiConfig.dCount,guiConfig.minPolar,guiConfig.maxPolar,guiConfig.maxCount)
lotusGroup.add(lotusInstanceGroup)
scene.add( lotusGroup)

function  mix(a,b,x) {
    return b*x + a*( 1-x)
}


/**
 * 莲蓬
 */
const seedPod = new Mesh();
/**
 * 
 * @param {number} height 茎秆 + 莲蓬的高度 
 * @param {number} b  莲蓬底部到顶端的距离
 * @param {number} r1  莲蓬的最大半径
 * @param {number} r2  茎秆半径
 * @param {number} bevel  倒角的距离
 * 
 * @param {number} segments1 
 * @param {number} segments2 
 */
function  updateSeedPod(height = 5,b =0.5 ,r1 = 4,r2=0.1 ,bevel = 0.1, segments1= 32,segments2= 16) {
    const profileCurve = new CircleCurve(r2,undefined,0,'z');
    const len = height,bottom =1- b/len,topBevel =(1 - bevel/len), maxRadius = r1,bevelRadius = maxRadius -bevel ;
    const mainCurve = new LineCurve3(new Vector3(0,-len,0),new Vector3(0,0,0));
    // @ts-ignore
    mainCurve.getRadiusArray = (x)=>{
        const radiusArray= []
        const node1 = (x * bottom), node2= x * topBevel;
        for ( let index = 0 ;index <= x;index++){
            if( index< node1) { radiusArray.push(1)}
            else if( index < node2){
                const factor = (index - node1 )/(node2 -node1) ;
                radiusArray.push( mix( r2,r1,factor)/r2)
            }else {
                const factor =  (index - node2 )/(x -node2) ;
                radiusArray.push(mix( maxRadius , bevelRadius,factor)/r2)
            }
        }
        return radiusArray;
    }

    const geo = curvesToMeshGeometry(mainCurve
        ,profileCurve,
        {
            segments1,
            segments2,
            cover:true
        }
    );  
    return geo;
}

function  updateSingleSeed(halfHeight = 0.1,radius= 0.06, segments1= 32,segments2= 8) {
    const profileCurve = new CircleCurve(radius,undefined,0,'z');
    // TS确实可以避免掉一些麻烦啊。
    const mainCurve = new LineCurve3(new Vector3(0,-halfHeight,0),new Vector3(0,halfHeight,0));
    const radiusArray= []
    // 这里就需要一个类似 倒二次函数的曲线
    function fx (x){ return (1-(x-1)**2 ) }
    // @ts-ignore
    mainCurve.getRadiusArray = (x)=>{
        for ( let index = 0 ;index <= x;index++){
            radiusArray.push( fx(index/x *2) );        
        }
        return radiusArray;
    }
    const geo = curvesToMeshGeometry(mainCurve
        ,profileCurve,
        {
            segments1,
            segments2,
            cover:true
        }
    );  
    return geo;
}
function initeSeedpod() {
    // 就用一个曲线转网格吧
   
   
    const seedPodConfig = {
        position:1.32,
        height:5,
        bottom:0.8,
        bevel:0.1,
        segments1:200,
        segments2:14,
        radius:0.4,//莲蓬半径
        radius2:0.05,//茎秆半径
        seedHeight:.06,
        seedRaius:0.05
    }
    const greenMat = new MeshStandardMaterial( {color:0x11aa11, roughness:0.8,side:DoubleSide})
    seedPod.material = greenMat;
    function updateSeedPodGeo() {
        if( seedPod.geometry){
            seedPod.geometry.dispose()
        }
        seedPod.geometry = updateSeedPod(seedPodConfig.height,seedPodConfig.bottom,seedPodConfig.radius,seedPodConfig.radius2,seedPodConfig.bevel,seedPodConfig.segments1,seedPodConfig.segments2)
    }
    updateSeedPodGeo();    
    console.log(seedPod);
    
    seedPod.position.set(0,seedPodConfig.position,0);
    lotusGroup.add(seedPod);

    const seedPodFolder = gui.addFolder('莲蓬');
    
    seedPodFolder.add(seedPodConfig,'position',-2,2, ).onChange((v)=>{
        seedPod.position.y = v;
    })
    seedPodFolder.add( seedPodConfig,'bevel',0.001,0.5).onChange(updateSeedPodGeo)
    seedPodFolder.add( seedPodConfig,'bottom',0.001,2).onChange(updateSeedPodGeo)
    seedPodFolder.add( seedPodConfig,'height',0.001,10).onChange(updateSeedPodGeo)
    seedPodFolder.add( seedPodConfig,'radius',0,3).onChange(updateSeedPodGeo).name('莲蓬半径')
    seedPodFolder.add( seedPodConfig,'radius2',0,0.5).onChange(updateSeedPodGeo).name('茎秆半径')
    seedPodFolder.add( seedPodConfig,'segments1',10,1000,1).onChange(updateSeedPodGeo).name('细分1')
    seedPodFolder.add( seedPodConfig,'segments2',3,32,1).onChange(updateSeedPodGeo).name('细分2')
    seedPodFolder.add( seedPodConfig,'radius2',0,0.5).onChange(updateSeedPodGeo).name('茎秆半径')

    // 莲子的分布要和莲蓬相适应
    // 两头尖的莲子，还是用曲线转网格
    const seedGeo = updateSingleSeed(seedPodConfig.seedHeight,seedPodConfig.seedRaius)
    const seedMesh = new Mesh(seedGeo,greenMat);
    seedPod.add( seedMesh);
    console.log(seedMesh);
    
    
    // 还是内圈和外圈放两层
    const outerRadius = seedPodConfig.radius - seedPodConfig.bevel,innerRadius = outerRadius* 0.6;
    const pos = new Vector3();
    let y=0,theta=0;
    const seedsPoints = []
    for (let index = 0; index < 10; index++) {
        theta = index/10 * PI *2 ;
        pos.set( innerRadius*cos(theta),y,-innerRadius*sin(theta) );        
        seedsPoints.push(pos.clone());
    }
    y = seedPodConfig.seedHeight -seedPodConfig.bevel;
    for (let index = 0; index < 16; index++) {
        theta = index/16 * PI *2 ;
        pos.set( outerRadius*cos(theta),y,-outerRadius*sin(theta) );        
        seedsPoints.push(pos.clone());
    }
    const seedsGroup = instanceObject3DOnPoints(seedMesh,seedsPoints)
    seedPod.add( seedsGroup);
    seedPodFolder.add( seedMesh.position,'y',-10,10)
}



initeSeedpod()


const strokeMat = new MeshNormalMaterial({
    wireframe: true  // 是否绘制为线框，默认false
})
let geo = curvesGeo;




const mesh3 = new Mesh(geo, strokeMat)
// 即便改成petals 也不是联动的 得直接改实例化的材质,不过重新创建荷花之后就是新的材质了，就这样吧
const mesh4 = petals

scene.add(mesh4,mesh3)




const lambertMat = new MeshLambertMaterial({
    color: 0xffffff,
    side: DoubleSide,
    // transparent: true,
    // opacity: .5,
    map ,
})

const phongMat = new MeshPhongMaterial({
    color: 0xffffff,
    side: DoubleSide,
    // transparent: true,
    
    // opacity: .5,
    map,
    shininess:100
})

const stdMat = new MeshStandardMaterial({
    color: 0xffffff,
    side: DoubleSide,
    // transparent: true,
    metalness:1,
    roughness:0.3,
    map
})


const pbrMat = new MeshPhysicalMaterial({
    color: 0xffffff,
    side: DoubleSide,
    // ior:1.3,
    transmission:1,
    thickness:1,
    roughness:0.1,
    reflectivity:.8,
    // metalness:1,
    // specular:0xffffff,
    // specularIntensity:1,
    // clearcoat:1,
    // clearcoatRoughness:0.1,
    // clearcoatNormalScale:1,
    // clearcoatNormalMap:map,
    // clearcoatNormalMapType:RepeatWrapping,
    // opacity: .5,
    // map
})
const petalMat = getPetalMat();
const matMap = {
    '基础':basicMat,
    "花瓣自发光":petalMat,
    '花瓣':pbrFlowerMat,
    '朗博':lambertMat,
    '冯氏':phongMat,
    '标准':stdMat,
    '物理':pbrMat
}
const matList = [
    
    '基础',
    '花瓣',
    '花瓣自发光',
    '朗博',
    '冯氏',
    '标准',
    '物理',
    
]

const board = new Mesh(new PlaneGeometry(2,2),pbrFlowerMat);
const scene2 =  new Scene();
scene2.add(new AmbientLight(),board)
const camera2 = new OrthographicCamera( -1, 1, 1, -1, 0, 10 )
camera2.position.z = 1;
const originSize = new Vector2()
function renderPetalMap() {
    renderer.getSize(originSize);
    renderer.clear()
    renderer.setSize(512,1024,false);
    renderer.render(scene2,camera2);
    canvasToImage(renderer.domElement,'petal.png')
    renderer.setSize(originSize.x,originSize.y,false);
}

// 现在是直接修改器原本的geometry的属性，为了保持引用
function updateCurveGeo() {
    if(curvesGeo) curvesGeo.dispose()
    const  profileCurve = new QuadraticBezierCurve3(new Vector3(0,curveParams.leafWidth,0),new Vector3(-curveParams.leafDepth,curveParams.leafMid,0),new Vector3(0,-curveParams.leafWidth,0))
    const  mainCurve = new QuadraticBezierCurve3(new Vector3(0,0,0),new Vector3(0,0,0),new Vector3(curveParams.leafLong,0,0))
    // @ts-ignore
    mainCurve.getRadius = getRadius;
    // @ts-ignore
    mainCurve.getRadiusArray = getRadiusArray;
    curvesGeo = curvesToMeshGeometry(mainCurve,profileCurve,{segments1:curveParams.segments1,segments2:curveParams.segments2,closed1:false})

    for(let attr of Object.keys(mesh3.geometry.attributes)){
        mesh3.geometry.attributes[attr] = curvesGeo.attributes[attr]
    }
    mesh3.geometry.index = curvesGeo.index


}

function updateCurveMesh() {
    updateCurveGeo();
}


curveMeshFolder.add(curveParams, 'segments1', 2, 32, 1).onChange(updateCurveMesh).name('花瓣分段')
curveMeshFolder.add(curveParams, 'segments2', 2, 32, 1).onChange(updateCurveMesh).name('管道分段')
curveMeshFolder.add(curveParams, 'leafLong', 0, 10, 0.1).onChange(updateCurveMesh).name('花瓣长度')
curveMeshFolder.add(curveParams, 'leafDepth', 0, 10, 0.1).onChange(updateCurveMesh).name('花瓣深度')
curveMeshFolder.add(curveParams, 'leafWidth', 0, 10, 0.1).onChange(updateCurveMesh).name('花瓣宽度')
curveMeshFolder.add(curveParams, 'leafMid', -1, 1, 0.1).onChange(updateCurveMesh).name('花瓣中间')
curveMeshFolder.add(flowerUniform.noiseScale, 'value', 0, 600, 1).onChange(updateCurveMesh).name('噪波缩放')
curveMeshFolder.add(flowerUniform.noiseOffset, 'value', 0, 300, 1).onChange(updateCurveMesh).name('噪声偏移')
curveMeshFolder.addColor(curveParams, 'bgColor').onChange((v) => {
    flowerUniform.bgColor.value.set(v);
    console.log(v,'color');
    
}).name('背景颜色')
curveMeshFolder.addColor(curveParams, 'color').onChange((v) => {
    pbrFlowerMat.color.set(v)
}).name('颜色')

function updateLotusGroup() {
    lotusGroup.remove(lotusInstanceGroup);
    // initFlower(guiConfig.layers,guiConfig.minRadius,guiConfig.maxRadius,guiConfig.dy,guiConfig.dCount,guiConfig.minPolar,guiConfig.maxPolar,guiConfig.maxCount)
    lotusInstanceGroup = initInstancedFlower(guiConfig.layers,guiConfig.minRadius,guiConfig.maxRadius,guiConfig.dy,guiConfig.dCount,guiConfig.minPolar,guiConfig.maxPolar,guiConfig.maxCount)
    lotusGroup.add(lotusInstanceGroup)

}
const lotusFolder = gui.addFolder('莲花参数')
lotusFolder.add(guiConfig, 'layers', 1, 10, 1).name('花瓣层数').onChange(updateLotusGroup)
lotusFolder.add(guiConfig, 'minRadius', 0.01, 0.5).name('最小半径').onChange(updateLotusGroup)
lotusFolder.add(guiConfig, 'maxRadius', 0.1, 1).name('最大半径').onChange(updateLotusGroup);
lotusFolder.add(guiConfig, 'dy', 0.01, 0.5).name('层高度间距').onChange(updateLotusGroup)
lotusFolder.add(guiConfig, 'dCount', 0, 5,1).name('层花瓣数量差值').onChange(updateLotusGroup)
lotusFolder.add(guiConfig, 'minPolar', 0, PI*.5).name('最小花瓣翘起角度').onChange(updateLotusGroup)
lotusFolder.add(guiConfig, 'maxPolar', 0, PI).name('最大花瓣翘起角度').onChange(updateLotusGroup)
lotusFolder.add(guiConfig, 'maxCount', 10, 100,1).name('最大花瓣数').onChange(updateLotusGroup)


export  const initLotus = (container) => {

    gui.add(axes, 'visible').name('显示坐标轴')
    gui.add(mesh3, 'visible').name('显示线框');
    gui.add(mesh4, 'visible').name('显示网面');
    gui.add(l1, 'intensity',0,2).name('补充光照');

        gui.add(guiConfig, 'mat', matList).name('材质')
        .onChange((item) => {
            mesh4.material = matMap[item];
            lotusInstanceGroup.traverse((child) => {
                if( child instanceof Mesh){
                    child.material = matMap[item];
                }
            })
        }).setValue('花瓣')
    

    gui.add(map.repeat, 'x', .001, 10).name('水平重复');
    gui.add(map.repeat, 'y', .001, 10).name('竖直重复');
    gui.add(guiConfig,'shot').name('截图')
    gui.add(guiConfig,'bake').name('烘焙纹理')
    gui.add(guiConfig,'save').name('导出')

    container.appendChild(renderer.domElement);
    stage.resize()
    stage.loopRender()
    stage.afterRender =()=>{
        if(guiConfig.needShot){
            canvasToImage(renderer.domElement,'莲花')
            guiConfig.needShot = false;
        }
        if(guiConfig.baking){
            renderPetalMap()
            guiConfig.baking = false;
        }
    }

}






